﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.IO.GML2
{
	public class GMLReader
	{
		protected IGeometryFactory Factory
		{
			get
			{
				return this._factory;
			}
		}

		public GMLReader() : this(GeometryFactory.Default)
		{
		}

		public GMLReader(IGeometryFactory factory)
		{
			this._factory = factory;
		}

		public IGeometry Read(XmlDocument document)
		{
			return this.Read(document.InnerXml);
		}

		public IGeometry Read(string xmlText)
		{
			return this.Read(new StringReader(xmlText));
		}

		public IGeometry Read(StringReader stringReader)
		{
			return this.Read(XmlReader.Create(stringReader));
		}

		public IGeometry Read(XmlReader reader)
		{
			if (reader.NodeType == XmlNodeType.EndElement)
			{
				throw new ArgumentException("EndElement node type cannot be read by GMLReader.");
			}
			if (GMLReader.IsStartElement(reader, "Point"))
			{
				return this.ReadPoint(reader);
			}
			if (GMLReader.IsStartElement(reader, "LineString"))
			{
				return this.ReadLineString(reader);
			}
			if (GMLReader.IsStartElement(reader, "Polygon"))
			{
				return this.ReadPolygon(reader);
			}
			if (GMLReader.IsStartElement(reader, "MultiPoint"))
			{
				return this.ReadMultiPoint(reader);
			}
			if (GMLReader.IsStartElement(reader, "MultiLineString"))
			{
				return this.ReadMultiLineString(reader);
			}
			if (GMLReader.IsStartElement(reader, "MultiPolygon"))
			{
				return this.ReadMultiPolygon(reader);
			}
			if (GMLReader.IsStartElement(reader, "MultiGeometry"))
			{
				return this.ReadGeometryCollection(reader);
			}
			reader.Read();
			return this.Read(reader);
		}

		protected Coordinate ReadCoordinate(XmlReader reader)
		{
			double x = 0.0;
			double y = 0.0;
			while (reader.Read())
			{
				XmlNodeType nodeType = reader.NodeType;
				if (nodeType != XmlNodeType.Element)
				{
					if (nodeType == XmlNodeType.EndElement)
					{
						if (reader.Name == "gml:coord")
						{
							return new Coordinate(x, y);
						}
					}
				}
				else if (GMLReader.IsStartElement(reader, "X"))
				{
					reader.Read();
					x = XmlConvert.ToDouble(reader.Value);
				}
				else if (GMLReader.IsStartElement(reader, "Y"))
				{
					reader.Read();
					y = XmlConvert.ToDouble(reader.Value);
				}
			}
			throw new ArgumentException("ShouldNeverReachHere!");
		}

		protected Coordinate ReadCoordinates(string value)
		{
			string[] array = value.Split(new char[]
			{
				','
			});
			double x = XmlConvert.ToDouble(array[0]);
			double y = XmlConvert.ToDouble(array[1]);
			return new Coordinate(x, y);
		}

		protected Coordinate ReadPosAsCoordinate(string[] value)
		{
			double[] array = new double[System.Math.Min(3, value.Length)];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = XmlConvert.ToDouble(value[i]);
			}
			if (array.Length != 2)
			{
				return new Coordinate(array[0], array[1], array[2]);
			}
			return new Coordinate(array[0], array[1]);
		}

		protected IEnumerable<Coordinate> ReadPosListAsCoordinates(int numOrdinates, string[] value)
		{
			Assert.IsTrue(value.Length % numOrdinates == 0);
			Coordinate[] array = new Coordinate[value.Length / numOrdinates];
			int num = 0;
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = new string[numOrdinates];
				Array.Copy(value, num, array2, 0, numOrdinates);
				num += numOrdinates;
				array[i] = this.ReadPosAsCoordinate(array2);
			}
			return array;
		}

		protected IPoint ReadPoint(XmlReader reader)
		{
			string attribute = reader.GetAttribute("srsDimension");
			while (reader.Read())
			{
				XmlNodeType nodeType = reader.NodeType;
				if (nodeType == XmlNodeType.Element)
				{
					if (GMLReader.IsStartElement(reader, "coord"))
					{
						return this.Factory.CreatePoint(this.ReadCoordinate(reader));
					}
					if (GMLReader.IsStartElement(reader, "pos"))
					{
						reader.Read();
						string[] array = reader.Value.Split(new char[]
						{
							' '
						}, StringSplitOptions.RemoveEmptyEntries);
						if (!string.IsNullOrEmpty(attribute))
						{
							int num = XmlConvert.ToInt32(attribute);
							Assert.IsTrue(array.Length == num, "srsDimension doen't match number of provided ordinates");
						}
						return this.Factory.CreatePoint(this.ReadPosAsCoordinate(array));
					}
					if (GMLReader.IsStartElement(reader, "coordinates"))
					{
						reader.Read();
						string[] array2 = reader.Value.Split(new char[]
						{
							' '
						}, StringSplitOptions.RemoveEmptyEntries);
						if (array2.Length != 1)
						{
							throw new ApplicationException("Should never reach here!");
						}
						Coordinate coordinate = this.ReadCoordinates(array2[0]);
						return this.Factory.CreatePoint(coordinate);
					}
				}
			}
			throw new ArgumentException("ShouldNeverReachHere!");
		}

		protected ILineString ReadLineString(XmlReader reader)
		{
			List<Coordinate> list = new List<Coordinate>();
			while (reader.Read())
			{
				XmlNodeType nodeType = reader.NodeType;
				if (nodeType != XmlNodeType.Element)
				{
					if (nodeType == XmlNodeType.EndElement)
					{
						return this.Factory.CreateLineString(list.ToArray());
					}
				}
				else if (GMLReader.IsStartElement(reader, "coord"))
				{
					list.Add(this.ReadCoordinate(reader));
				}
				else if (GMLReader.IsStartElement(reader, "pos"))
				{
					reader.Read();
					string text = reader.ReadContentAsString();
					list.Add(this.ReadPosAsCoordinate(text.Split(new char[]
					{
						' '
					}, StringSplitOptions.RemoveEmptyEntries)));
				}
				else
				{
					if (GMLReader.IsStartElement(reader, "coordinates"))
					{
						reader.Read();
						foreach (string value in reader.Value.Replace("\n", " ").Replace("\t", " ").Split(new char[]
						{
							' '
						}, StringSplitOptions.RemoveEmptyEntries))
						{
							if (!string.IsNullOrEmpty(value))
							{
								Coordinate item = this.ReadCoordinates(value);
								list.Add(item);
							}
						}
						return this.Factory.CreateLineString(list.ToArray());
					}
					if (GMLReader.IsStartElement(reader, "posList"))
					{
						string text2 = reader.GetAttribute("srsDimension");
						if (string.IsNullOrEmpty(text2))
						{
							text2 = "2";
						}
						reader.Read();
						list.AddRange(this.ReadPosListAsCoordinates(XmlConvert.ToInt32(text2), reader.ReadContentAsString().Split(new char[]
						{
							' '
						}, StringSplitOptions.RemoveEmptyEntries)));
						reader.ReadEndElement();
						return this.Factory.CreateLineString(list.ToArray());
					}
				}
			}
			throw new ArgumentException("ShouldNeverReachHere!");
		}

		protected ILinearRing ReadLinearRing(XmlReader reader)
		{
			return this.Factory.CreateLinearRing(this.ReadLineString(reader).Coordinates);
		}

		protected IPolygon ReadPolygon(XmlReader reader)
		{
			ILinearRing shell = null;
			List<ILinearRing> list = new List<ILinearRing>();
			while (reader.Read())
			{
				XmlNodeType nodeType = reader.NodeType;
				if (nodeType != XmlNodeType.Element)
				{
					if (nodeType == XmlNodeType.EndElement)
					{
						string name = reader.Name;
						if (name == "Polygon" || name == "gml:Polygon")
						{
							return this.Factory.CreatePolygon(shell, list.ToArray());
						}
					}
				}
				else if (GMLReader.IsStartElement(reader, "outerBoundaryIs") || GMLReader.IsStartElement(reader, "exterior"))
				{
					shell = this.ReadLinearRing(reader);
				}
				else if (GMLReader.IsStartElement(reader, "innerBoundaryIs") || GMLReader.IsStartElement(reader, "interior"))
				{
					list.Add(this.ReadLinearRing(reader));
				}
			}
			throw new ArgumentException("ShouldNeverReachHere!");
		}

		protected IMultiPoint ReadMultiPoint(XmlReader reader)
		{
			List<IPoint> list = new List<IPoint>();
			while (reader.Read())
			{
				XmlNodeType nodeType = reader.NodeType;
				if (nodeType != XmlNodeType.Element)
				{
					if (nodeType == XmlNodeType.EndElement)
					{
						string name = reader.Name;
						if (name == "MultiPoint" || name == "gml:MultiPoint")
						{
							return this.Factory.CreateMultiPoint(list.ToArray());
						}
					}
				}
				else if (GMLReader.IsStartElement(reader, "pointMember"))
				{
					list.Add(this.ReadPoint(reader));
				}
			}
			throw new ArgumentException("ShouldNeverReachHere!");
		}

		protected IMultiLineString ReadMultiLineString(XmlReader reader)
		{
			List<ILineString> list = new List<ILineString>();
			while (reader.Read())
			{
				XmlNodeType nodeType = reader.NodeType;
				if (nodeType != XmlNodeType.Element)
				{
					if (nodeType == XmlNodeType.EndElement)
					{
						string name = reader.Name;
						if (name == "MultiLineString" || name == "gml:MultiLineString")
						{
							return this.Factory.CreateMultiLineString(list.ToArray());
						}
					}
				}
				else if (GMLReader.IsStartElement(reader, "lineStringMember"))
				{
					list.Add(this.ReadLineString(reader));
				}
			}
			throw new ArgumentException("ShouldNeverReachHere!");
		}

		protected IMultiPolygon ReadMultiPolygon(XmlReader reader)
		{
			List<IPolygon> list = new List<IPolygon>();
			while (reader.Read())
			{
				XmlNodeType nodeType = reader.NodeType;
				if (nodeType != XmlNodeType.Element)
				{
					if (nodeType == XmlNodeType.EndElement)
					{
						string name = reader.Name;
						if (name == "MultiPolygon" || name == "gml:MultiPolygon")
						{
							return this.Factory.CreateMultiPolygon(list.ToArray());
						}
					}
				}
				else if (GMLReader.IsStartElement(reader, "polygonMember"))
				{
					list.Add(this.ReadPolygon(reader));
				}
			}
			throw new ArgumentException("ShouldNeverReachHere!");
		}

		protected IGeometryCollection ReadGeometryCollection(XmlReader reader)
		{
			List<IGeometry> list = new List<IGeometry>();
			while (reader.Read())
			{
				XmlNodeType nodeType = reader.NodeType;
				if (nodeType != XmlNodeType.Element)
				{
					if (nodeType == XmlNodeType.EndElement)
					{
						string name = reader.Name;
						if (name == "MultiGeometry" || name == "gml:MultiGeometry")
						{
							return this.Factory.CreateGeometryCollection(list.ToArray());
						}
					}
				}
				else if (GMLReader.IsStartElement(reader, "Point"))
				{
					list.Add(this.ReadPoint(reader));
				}
				else if (GMLReader.IsStartElement(reader, "LineString"))
				{
					list.Add(this.ReadLineString(reader));
				}
				else if (GMLReader.IsStartElement(reader, "Polygon"))
				{
					list.Add(this.ReadPolygon(reader));
				}
				else if (GMLReader.IsStartElement(reader, "MultiPoint"))
				{
					list.Add(this.ReadMultiPoint(reader));
				}
				else if (GMLReader.IsStartElement(reader, "MultiLineString"))
				{
					list.Add(this.ReadMultiLineString(reader));
				}
				else if (GMLReader.IsStartElement(reader, "MultiPolygon"))
				{
					list.Add(this.ReadMultiPolygon(reader));
				}
				else if (GMLReader.IsStartElement(reader, "MultiGeometry"))
				{
					list.Add(this.ReadGeometryCollection(reader));
				}
			}
			throw new ArgumentException("ShouldNeverReachHere!");
		}

		private static bool IsStartElement(XmlReader reader, string name)
		{
			return reader.IsStartElement(name) || reader.IsStartElement(name, "http://www.opengis.net/gml") || reader.IsStartElement("gml:" + name);
		}

		private readonly IGeometryFactory _factory;
	}
}
